/*
 http文件下载
 1、搭建http服务器，可以使用Nginx或者apache
 2、客户端编程，接收网络发送的内容，写入本地文件，
  （下载就是在本地新建一个文件，然后将收到的网络传输内容写到文件中）

*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>

#define BUFFER_SIZE     4096
#define PORT 8080


int parse_url(const char *url, char *host, char *path, int *port) {
    char *p;

    printf("url %s\n", url);
    if (strstr(url, "http://") == url) {
        p = (char *)url +7;     // 去除 http://
    } else {
        p = (char *) url;
    }

    // 主机名和路径
    char *path_start = strchr(p, '/');
    if (path_start != NULL) {
        strncpy(host, p, path_start - p);
        host[path_start - p] = '\0';
        printf("parse_url host %s\n", host);
        strcpy(path, path_start);
    } else {
        strcpy(host, p);
        strcpy(path, "/");
    }
    
    // 端口号
    char *port_start = strchr(host, ':');
    if (port_start != NULL) {
        *port = atoi(port_start + 1);
        *port_start = '\0';
    } else {
        *port = PORT;
    }

    return 0;
}

char* get_file_name_from_url(const char *url) {
    const char *last_slash = strrchr(url, '/');
    if (last_slash && (last_slash + 1) != '/0') {
        return (char *)(last_slash + 1);
    }
    return NULL;    // 文件名解析失败
}

char* get_file_name_from_headers(const char *headers) {
    printf("headers %s\n", headers);
    char content_disposition = strstr(headers, "Content--Disposition:");
    if (content_disposition) {
        char *filenema_start = strstr(content_disposition, "filename=");
        if (filenema_start) {
            filenema_start += 9; 

            char quote = *filenema_start;
            if (quote == '"' || quote == '\'') {
                filenema_start ++;
                char *filename_end = strchr(filenema_start, quote);
                if (filename_end) {
                    static char filename[256];
                    strncpy(filename, filenema_start, filename_end - filenema_start);
                    filename[filename_end - filenema_start] = "\0";
                    return filename;
                }
            } else {
                // 没有引号的情况
                char *filename_end = strchr(filenema_start, ';');
                if (!filename_end) {
                    filename_end = strchr(filenema_start, '\r');
                    if (filename_end) {
                        static char filename[256];
                        strncpy(filename, filenema_start, filename_end - filenema_start);
                        filename[filename_end - filenema_start] = '\0';
                        return filename;
                    }
                }
            }
        }
    }
    return NULL;
}


int download_file(const char *url, const char *local_filename) {
    int socketfd;
    struct sockaddr_in server_addr;
    struct hostent *server;
    char request[1024];
    char respoese[BUFFER_SIZE];
    char host[256];
    char path[512];
    int port;

    // 解析url
    parse_url(url, host, path, &port);
    printf("host:%s, port:%d, path:%s\n", host, port, path);

    socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd < 0) {
        error("socket创建失败");
        return -1;
    }

    // 获取服务器信息
    printf("host %s\n", host);
    server = gethostbyname(host);
    if (server == NULL) {
        fprintf(stderr, "ERROR, no such host\n");
        close(socketfd);
        return -1;
    }

    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    memcpy(&server_addr.sin_addr.s_addr, server->h_addr_list[0], server->h_length);
    server_addr.sin_port = htons(port);

    // 连接服务器
    if (connect(socketfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        error("服务器连接失败");
    }

    // 构造http请求
    snprintf(request, sizeof(request),
             "GET %s HTTP/1.1\r\n"
             "Host: %s\r\n"
             "User-Agent: C-Downloader/1.0\r\n"
             "Connection: Close\r\n"
             "\r\n",
             path, host);

    // 发送HTTP请求
    int n = write(socketfd, request, strlen(request));
    if (n < 0) {
        error("ERROR writing to socket");
    }
    
    // 读取HTTP响应
    FILE *output_file = fopen(local_filename, "wb");
    if (output_file == NULL) {
        error("local_name open failed");
    }

    int headers_received = 0;
    int content_length = -1;
    int total_received = 0;
    char *filename_from_headers = NULL;


    while(1) {
        n = read(socketfd, respoese, BUFFER_SIZE - 1);
        printf("n = %d\n",n);
        if (n == 0) {   // 关闭连接
            break;
        } else if (n < 0) {
            error("read failed");
        }

        if (!headers_received) {
            respoese[n] = '\0';

            // 查找http头结束位置
            char *header_end = strstr(respoese, "\r\n\r\n");
            printf("header_end %s\n",header_end);
            if (header_end) {
                headers_received = 1;
                int header_length = header_end - respoese + 4;

                if (strstr(respoese, "200 OK") == NULL) {
                    printf("HTTP Error: %s\n", respoese);
                    fclose(output_file);
                    close(socketfd);
                    remove(local_filename);
                    return -1;
                }

                filename_from_headers = get_file_name_from_headers(respoese);

                fwrite(header_end + 4, 1, n - header_length, output_file);
                total_received += n - header_length;
            }
        } else {
            fwrite(respoese, 1, n, output_file);
            total_received += n;
        }
    }
    
    fclose(output_file);
    close(socketfd);

    printf("Download completed: %s (%d bytes)\n", local_filename, total_received);
    if (filename_from_headers != NULL && strcmp(filename_from_headers, local_filename) != 0) {
        if (rename(local_filename, filename_from_headers) == 0) {
            printf("File renamed to: %s\n", filename_from_headers);
        }
    }

    return 0;
}

int main(int argc, char *argv[]) {
    if (argc < 2 ) {
        printf("Usage: %s <URL> [output_filename]\n", argv[0]);
        printf("Example: %s http://localhost:8080/file.zip\n", argv[0]);
        return 1;
    }

    const char *url = argv[1];
    const char *filename;

    if (argc == 3) {
        filename = argv[2];
    } else {
        filename = get_file_name_from_url(url);
    }

    printf("String download from:  %s\n", url);
    printf("Saving to: %s \n", filename);

    if (download_file(url, filename) == 0) {
        printf("Download successful!\n");
    } else {
        printf("Download failed! \n");
        return 1;
    }

    return 0;
}